﻿// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
// 
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using System.Data.Services.Client;
using System.Xml.Linq;
using System.Data.Services.Common;
using System.Reflection;
using EncryptDecrypt;
namespace System.Data.Services.Common
{
    /// <summary>
    /// Properties marked with this Attribute are not serialized in the payload when sent to the server
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class DoNotSerializeAttribute : Attribute
    {
    }

    /// <summary>
    /// Properties marked with this Attribute are encrypted when sent to the server
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class EncryptAttribute : Attribute
    {
    }

    /// <summary>
    /// Properties marked with this Attribute are used as a seed for Symmetric Encryption
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class EncryptSeedAttribute : Attribute
    {
    }
}
namespace RdChat_WebRole
{
    public class MessageDataServiceContext   : TableServiceContext
    {
        public MessageDataServiceContext(string baseAddress, StorageCredentials credentials)
            : base(baseAddress, credentials)
        {
            this.WritingEntity += new EventHandler<ReadingWritingEntityEventArgs>(DataServiceContextEx_WritingEntity);
            this.ReadingEntity += new EventHandler<ReadingWritingEntityEventArgs>(DataServiceContextEx_ReadingEntity);

             this.azCrypto = new EncryptDecrypt.AzureTableCrypto(EncryptionStrengthEnum.VeryWeak, baseAddress, credentials);

            // Todo: CHANGE THIS: Replace this certificate hash with your certificate.  You can use your SSL key for simplicity
             this.azCrypto.LoadCertificateByThumbprint("‎1e c3 26 00 da 34 05 c9 44 c7 75 18 84 c7 8b a7 e4 f4 ed 8b");
        }
         
        AzureTableCrypto azCrypto = null;

        /// <summary>
        /// The following code is from:
        /// http://social.msdn.microsoft.com/Forums/en-US/windowsazuredata/thread/5ba62aa0-46c1-4599-b44f-8df4798cacad
        /// 
        /// Much appreciation to Wenchao Zeng!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DataServiceContextEx_ReadingEntity(object sender, ReadingWritingEntityEventArgs e)
        {
            // Todo: Next Version: support more than one version of Key
            int KeyVersion = 0;
             

            foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
            {
                object[] transparentEncryptAttributes = property.GetCustomAttributes(typeof(EncryptAttribute), false);
                if (transparentEncryptAttributes.Length > 0 && property.PropertyType == typeof(string))
                {
                    string propertyValue = (string)property.GetValue(e.Entity, null);
                    propertyValue = azCrypto.DecryptString(propertyValue, 0);
                    property.SetValue(e.Entity, propertyValue, null);
                }
            }
        }

        /// <summary>
        ///  The following code is from:
        ///  http://blogs.msdn.com/b/phaniraj/archive/2008/12/11/customizing-serialization-of-entities-in-the-ado-net-data-services-client-library.aspx
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DataServiceContextEx_WritingEntity(object sender, ReadingWritingEntityEventArgs e)
        {
            // e.Data gives you the XElement for the Serialization of the Entity 
            //Using XLinq  , you can  add/Remove properties to the element Payload  
            XName xnEntityProperties = XName.Get("properties", e.Data.GetNamespaceOfPrefix("m").NamespaceName);
            XElement xePayload = null;
            foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
            {
                object[] doNotSerializeAttributes = property.GetCustomAttributes(typeof(DoNotSerializeAttribute), false);
                if (doNotSerializeAttributes.Length > 0)
                {
                    if (xePayload == null)
                    {
                        xePayload = e.Data.Descendants().Where<XElement>(xe => xe.Name == xnEntityProperties).First<XElement>();
                    }
                    //The XName of the property we are going to remove from the payload
                    XName xnProperty = XName.Get(property.Name, e.Data.GetNamespaceOfPrefix("d").NamespaceName);
                    //Get the Property of the entity  you don't want sent to the server
                    XElement xeRemoveThisProperty = xePayload
                                   .Descendants()
                                   .Where<XElement>(xe => xe.Name == xnProperty)
                                   .First<XElement>();
                    //Remove this property from the Payload sent to the server 
                    xeRemoveThisProperty.Remove();
                }

                object[] transparentEncryptAttributes = property.GetCustomAttributes(typeof(EncryptAttribute), false);
                if (transparentEncryptAttributes.Length > 0 )
                {
                    if (xePayload == null)
                    {
                        xePayload = e.Data.Descendants().Where<XElement>(xe => xe.Name == xnEntityProperties).First<XElement>();
                    }
                    //The XName of the property we are going to encrypt from the payload
                    XName xnProperty = XName.Get(property.Name, e.Data.GetNamespaceOfPrefix("d").NamespaceName);
                    //Get the Property of the entity you want to encrypt on the server
                    XElement xeEncryptThisProperty = xePayload
                                   .Descendants()
                                   .Where<XElement>(xe => xe.Name == xnProperty)
                                   .First<XElement>();

                    //Encrypt this property 
                    xeEncryptThisProperty.Value = azCrypto.EncryptString(xeEncryptThisProperty.Value,null).DataBase64String;
                }
            }
        } 

        public IQueryable<Message> Messages
        {
            get
            {
                return this.CreateQuery<Message>("Messages");
            }
        }

        public void AddMessage(string name, string body)
        {
            this.AddObject("Messages", new Message { Name = name, Body = body ,
                skipthis = "not sent to cloud...",  BodyCopy = body}); 
            this.SaveChanges();
        }
    }
}